/*
* Red Bee Browser
*
* Copyright (c) 2013 Tran Dinh Thoai <dthoai@yahoo.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.redbee.schema;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.NumericField;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
public class LuceneHandler extends Entity.Handler {
protected String dirIndex = "";
protected String dirBackup = "";
public LuceneHandler(String folder) {
this.dirIndex = new File(folder, "index").getAbsolutePath();
this.dirBackup = new File(folder, "backup").getAbsolutePath();
new File(dirIndex).mkdirs();
new File(dirBackup).mkdirs();
}
public boolean exists(String id) {
boolean tag = false;
if (id.length() == 0) return tag;
try {
IndexReader reader = IndexReader.open(FSDirectory.open(new File(dirIndex)));
IndexSearcher searcher = new IndexSearcher(reader);
TopDocs td = searcher.search(new TermQuery(new Term(Entity.ID, id)), 1);
if (td.totalHits > 0) {
tag = true;
}
searcher.close();
reader.close();
} catch (Exception e) {
}
return tag;
}
public void create(Entity src) {
Monitor monitor = new Monitor();
Timer timer = new Timer();
timer.schedule(new CreateTask(timer, src, monitor), 1);
while (!monitor.finished) {
try {
Thread.sleep(10);
} catch (Exception e) {
}
}
timer = null;
}
protected void createEntity(Entity src) {
if (src.getId().length() == 0) return;
if (src.getKind().length() == 0) return;
try {
backup(src);
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_36);
IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_36, analyzer);
iwc.setOpenMode(OpenMode.CREATE_OR_APPEND);
IndexWriter writer = new IndexWriter(FSDirectory.open(new File(dirIndex)), iwc);
Document doc = new Document();
write(src, doc);
writer.addDocument(doc);
writer.close();
} catch (Exception e) {
}
}
public void update(Entity src) {
Monitor monitor = new Monitor();
Timer timer = new Timer();
timer.schedule(new UpdateTask(timer, src, monitor), 1);
while (!monitor.finished) {
try {
Thread.sleep(10);
} catch (Exception e) {
}
}
timer = null;
}
protected void updateEntity(Entity src) {
if (src.getId().length() == 0) return;
if (src.getKind().length() == 0) return;
try {
backup(src);
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_36);
IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_36, analyzer);
iwc.setOpenMode(OpenMode.CREATE_OR_APPEND);
IndexWriter writer = new IndexWriter(FSDirectory.open(new File(dirIndex)), iwc);
Document doc = new Document();
write(src, doc);
writer.updateDocument(new Term(Entity.ID, src.getId()), doc);
writer.close();
} catch (Exception e) {
}
}
public void load(String id, Entity src) {
try {
IndexReader reader = IndexReader.open(FSDirectory.open(new File(dirIndex)));
IndexSearcher searcher = new IndexSearcher(reader);
TopDocs td = searcher.search(new TermQuery(new Term(Entity.ID, id)), 1);
if (td.totalHits > 0) {
Document doc = searcher.doc(td.scoreDocs[0].doc);
if (allowLoad(id, doc.get(Entity.KIND))) {
src.setSchema(doc.get(Entity.SCHEMA));
read(src, doc);
}
}
searcher.close();
reader.close();
} catch (Exception e) {
}
}
protected boolean allowLoad(String id, String kind) {
return true;
}
public int count(String kind, Query query, Filter filter, Sort sort, int max) {
int tag = 0;
try {
IndexReader reader = IndexReader.open(FSDirectory.open(new File(dirIndex)));
IndexSearcher searcher = new IndexSearcher(reader);
BooleanQuery boolQuery = new BooleanQuery();
boolQuery.add(new BooleanClause(new TermQuery(new Term(Entity.KIND, kind)), Occur.MUST));
if (query != null) {
boolQuery.add(new BooleanClause(query, Occur.MUST));
}
TopDocs td = null;
if (filter != null && sort != null) {
td = searcher.search(boolQuery, filter, max, sort);
} else if (filter != null) {
td = searcher.search(boolQuery, filter, max);
} else if (sort != null) {
td = searcher.search(boolQuery, max, sort);
} else {
td = searcher.search(boolQuery, max);
}
tag = td.totalHits;
searcher.close();
reader.close();
} catch (Exception e) {
}
return tag;
}
public int count(String kind, Query query, int max) {
return count(kind, query, null, null, max);
}
public int count(String kind, Query query, Sort sort, int max) {
return count(kind, query, null, sort, max);
}
public int count(String kind, Query query, Filter filter, int max) {
return count(kind, query, filter, null, max);
}
public List<Entity> search(String kind, Query query, int max) {
return search(kind, query, null, null, max);
}
public List<Entity> search(String kind, Query query, Sort sort, int max) {
return search(kind, query, null, sort, max);
}
public List<Entity> search(String kind, Query query, Filter filter, int max) {
return search(kind, query, filter, null, max);
}
public List<Entity> search(String kind, Query query, int pagesize, int pageno) {
return search(kind, query, null, null, pagesize, pageno);
}
public List<Entity> search(String kind, Query query, Sort sort, int pagesize, int pageno) {
return search(kind, query, null, sort, pagesize, pageno);
}
public List<Entity> search(String kind, Query query, Filter filter, int pagesize, int pageno) {
return search(kind, query, filter, null, pagesize, pageno);
}
public List<Entity> search(String kind, Query query, Filter filter, Sort sort, int max) {
List<Entity> tag = new ArrayList<Entity>();
try {
IndexReader reader = IndexReader.open(FSDirectory.open(new File(dirIndex)));
IndexSearcher searcher = new IndexSearcher(reader);
BooleanQuery boolQuery = new BooleanQuery();
boolQuery.add(new BooleanClause(new TermQuery(new Term(Entity.KIND, kind)), Occur.MUST));
if (query != null) {
boolQuery.add(new BooleanClause(query, Occur.MUST));
}
TopDocs td = null;
if (filter != null && sort != null) {
td = searcher.search(boolQuery, filter, max, sort);
} else if (filter != null) {
td = searcher.search(boolQuery, filter, max);
} else if (sort != null) {
td = searcher.search(boolQuery, max, sort);
} else {
td = searcher.search(boolQuery, max);
}
for (int i = 0; i < td.totalHits; i++) {
Entity item = new Entity(this);
Document doc = searcher.doc(td.scoreDocs[i].doc);
item.setSchema(doc.get(Entity.SCHEMA));
read(item, doc);
tag.add(item);
}
searcher.close();
reader.close();
} catch (Exception e) {
}
return tag;
}
public List<Entity> search(String kind, Query query, Filter filter, Sort sort, int pagesize, int pageno) {
List<Entity> tag = new ArrayList<Entity>();
try {
IndexReader reader = IndexReader.open(FSDirectory.open(new File(dirIndex)));
IndexSearcher searcher = new IndexSearcher(reader);
BooleanQuery boolQuery = new BooleanQuery();
boolQuery.add(new BooleanClause(new TermQuery(new Term(Entity.KIND, kind)), Occur.MUST));
if (query != null) {
boolQuery.add(new BooleanClause(query, Occur.MUST));
}
if (pagesize <= 0) pagesize = 10;
if (pageno <= 0) pageno = 1;
int max = pageno * pagesize;
TopDocs td = null;
if (filter != null && sort != null) {
td = searcher.search(boolQuery, filter, max, sort);
} else if (filter != null) {
td = searcher.search(boolQuery, filter, max);
} else if (sort != null) {
td = searcher.search(boolQuery, max, sort);
} else {
td = searcher.search(boolQuery, max);
}
for (int i = (pageno - 1) * pagesize; i < td.totalHits && i < max; i++) {
Entity item = new Entity(this);
Document doc = searcher.doc(td.scoreDocs[i].doc);
item.setSchema(doc.get(Entity.SCHEMA));
read(item, doc);
tag.add(item);
}
searcher.close();
reader.close();
} catch (Exception e) {
}
return tag;
}
protected void backup(Entity src) {
String id = src.getId();
if (id.length() == 0) return;
String kind = src.getKind();
if (kind.length() == 0) return;
String fid = "";
for (int i = 0; i < id.length() && i + 1 < id.length(); i += 2) {
if (fid.length() > 0) fid += File.separator;
fid += id.substring(i, i + 2);
}
try {
File file = new File(dirBackup, kind);
file = new File(file.getAbsolutePath(), fid);
file.mkdirs();
String folder = file.getAbsolutePath();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(folder, id + ".txt"))));
writer.write(src.toString());
writer.close();
} catch (Exception e) {
}
}
protected void read(Entity entity, Document doc) {
String schema = doc.get(Entity.SCHEMA);
if (schema == null) schema = "";
String[] fields = schema.split("\\|");
for (int i = 0; i < fields.length && i + 1 < fields.length; i+= 2) {
String kind = fields[i];
String fname = fields[i + 1];
String val = doc.get(fname);
if (val == null) val = "";
if (Entity.ALL_KINDS.indexOf("|" + kind + "|") < 0) continue;
entity.setString(fname, val);
}
}
protected void write(Entity entity, Document doc) {
String schema = entity.getSchema();
if (schema == null) schema = "";
String[] fields = schema.split("\\|");
for (int i = 0; i < fields.length && i + 1 < fields.length; i+= 2) {
String kind = fields[i];
String fname = fields[i + 1];
if (Entity.STRING.equalsIgnoreCase(kind)) {
Field field = new Field(fname, entity.getString(fname), Store.YES, Index.NOT_ANALYZED_NO_NORMS);
doc.add(field);
} else if (Entity.DOUBLE.equalsIgnoreCase(kind)) {
NumericField field = new NumericField(fname, Store.YES, true);
field.setDoubleValue(entity.getDouble(fname));
doc.add(field);
} else if (Entity.FLOAT.equalsIgnoreCase(kind)) {
NumericField field = new NumericField(fname, Store.YES, true);
field.setFloatValue(entity.getFloat(fname));
doc.add(field);
} else if (Entity.INTEGER.equalsIgnoreCase(kind)) {
NumericField field = new NumericField(fname, Store.YES, true);
field.setIntValue(entity.getInteger(fname));
doc.add(field);
} else if (Entity.LONG.equalsIgnoreCase(kind)) {
NumericField field = new NumericField(fname, Store.YES, true);
field.setLongValue(entity.getLong(fname));
doc.add(field);
} else if (Entity.ANALYZED.equalsIgnoreCase(kind)) {
Field field = new Field(fname, entity.getString(fname), Store.YES, Index.ANALYZED);
doc.add(field);
}
}
}
public void delete(String id) {
Monitor monitor = new Monitor();
Timer timer = new Timer();
timer.schedule(new DeleteTask(timer, id, monitor), 1);
while (!monitor.finished) {
try {
Thread.sleep(10);
} catch (Exception e) {
}
}
timer = null;
}
protected void deleteEntity(String id) {
if (id.length() == 0) return;
String kind = "";
try {
IndexReader reader = IndexReader.open(FSDirectory.open(new File(dirIndex)));
IndexSearcher searcher = new IndexSearcher(reader);
TopDocs td = searcher.search(new TermQuery(new Term(Entity.ID, id)), 1);
if (td.totalHits > 0) {
Document doc = searcher.doc(td.scoreDocs[0].doc);
kind = doc.get(Entity.KIND);
}
searcher.close();
reader.close();
} catch (Exception e) {
}
if (kind.length() == 0) return;
if (!allowDelete(id, kind)) return;
try {
removeBackup(id, kind);
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_36);
IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_36, analyzer);
iwc.setOpenMode(OpenMode.CREATE_OR_APPEND);
IndexWriter writer = new IndexWriter(FSDirectory.open(new File(dirIndex)), iwc);
writer.deleteDocuments(new Term(Entity.ID, id));
writer.close();
} catch (Exception e) {
}
}
protected boolean allowDelete(String id, String kind) {
return true;
}
protected void removeBackup(String id, String kind) {
if (id.length() == 0) return;
if (kind.length() == 0) return;
String fid = "";
for (int i = 0; i < id.length() && i + 1 < id.length(); i += 2) {
if (fid.length() > 0) fid += File.separator;
fid += id.substring(i, i + 2);
}
try {
File file = new File(dirBackup, kind);
file = new File(file.getAbsolutePath(), fid);
String folder = file.getAbsolutePath();
file = new File(folder, id + ".txt");
file.delete();
} catch (Exception e) {
}
}
private class DeleteTask extends TimerTask {
private String id;
private Timer timer;
private Monitor monitor;
public DeleteTask(Timer timer, String id, Monitor monitor) {
this.timer = timer;
this.id = id;
this.monitor = monitor;
}
@Override
public void run() {
deleteEntity(id);
monitor.finished = true;
timer.cancel();
timer.purge();
timer = null;
}
}
private class CreateTask extends TimerTask {
private Entity entity;
private Timer timer;
private Monitor monitor;
public CreateTask(Timer timer, Entity entity, Monitor monitor) {
this.timer = timer;
this.entity = entity;
this.monitor = monitor;
}
@Override
public void run() {
createEntity(entity);
monitor.finished = true;
timer.cancel();
timer.purge();
timer = null;
}
}
private class UpdateTask extends TimerTask {
private Entity entity;
private Timer timer;
private Monitor monitor;
public UpdateTask(Timer timer, Entity entity, Monitor monitor) {
this.timer = timer;
this.entity = entity;
this.monitor = monitor;
}
@Override
public void run() {
updateEntity(entity);
monitor.finished = true;
timer.cancel();
timer.purge();
timer = null;
}
}
private class Monitor {
public boolean finished = false;
}
}